package de.bk.timecalc.console;

import static de.bk.timecalc.i18n.I18nKeys.*;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.text.NumberFormat;
import java.util.Locale;

import de.bk.timecalc.core.ChallengeDTO;
import de.bk.timecalc.core.TimeCalcCore;
import de.bk.timecalc.core.TimeDTO;
import de.bk.timecalc.core.TimerDirection;
import de.bk.timecalc.i18n.StringFactory;

/**
 * Command line version client for QL Time Calc.
 * 
 * @author Bastian Krol
 * @version $Revision:$ $Date:$ $Author:$
 */
public class Console
{
  private static final NumberFormat FORMAT = NumberFormat.getInstance();
  private static String HELP;
  static
  {
    FORMAT.setMinimumFractionDigits(3);
    FORMAT.setMaximumFractionDigits(3);
    FORMAT.setGroupingUsed(true);

    try
    {
      URL helpUrl = Console.class.getResource("help.txt");
      InputStream stream = helpUrl.openStream();
      BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
      StringBuffer help = new StringBuffer();
      String line;
      while ((line = reader.readLine()) != null)
      {
        help.append(line);
        help.append("\r\n");
      }
      HELP = help.toString();
    }
    catch (Throwable t)
    {
      HELP = "Sorry, no help available. Cause: " + t.getLocalizedMessage();
    }
  }

  private TimeCalcCore timeCalcCore;
  private BufferedReader input;
  private LineParser lineParser;
  private StringFactory stringFactory;
  private ConsolePropertiesReader consolePropertiesReader;

  public Console()
  {
    this.timeCalcCore = new TimeCalcCore();
    this.input = new BufferedReader(new InputStreamReader(System.in));
    this.lineParser = new LineParser();
    this.consolePropertiesReader = new ConsolePropertiesReader();
    this.initializeStringFactory();
    try
    {
      this.consolePropertiesReader.readProperties();
    }
    catch (FileNotFoundException e)
    {
      // no properties present, that's okay
      System.out.println(e.getMessage());
      System.out.println(this.getString(WELCOME));
    }
    this.timeCalcCore.setTimerDirection(this.consolePropertiesReader
        .getTimerDirection());
  }

  private void initializeStringFactory()
  {
    // initialize StringFactory with current locale so error messages while
    // loading the properties file are displayed in the current locale
    this.stringFactory = (StringFactory) StringFactory.getInstance();
    this.stringFactory.loadBundleForCurrentLocale();

    String localeString = this.consolePropertiesReader.getLocaleString();
    if (localeString != null)
    {
      String[] localeParts = localeString.split("_");
      if (localeParts.length >= 3)
      {
        StringFactory.getInstance().loadBundleForLocale(
            new Locale(localeParts[0], localeParts[1], localeParts[2]));
        System.out
            .println("Locale: " + StringFactory.getInstance().getLocale());
        System.out.println(this.getString(WELCOME));
      }
      else if (localeParts.length == 2)
      {
        StringFactory.getInstance().loadBundleForLocale(
            new Locale(localeParts[0], localeParts[1]));
        System.out
            .println("Locale: " + StringFactory.getInstance().getLocale());
        System.out.println(this.getString(WELCOME));
      }
      else if (localeParts.length == 1)
      {
        StringFactory.getInstance().loadBundleForLocale(
            new Locale(localeParts[0]));
        System.out
            .println("Locale: " + StringFactory.getInstance().getLocale());
        System.out.println(this.getString(WELCOME));
      }
      else
      {
        System.out
            .println("Locale String from config file could not be parsed: "
                + localeString);
        System.out
            .println("Locale: " + StringFactory.getInstance().getLocale());
        System.out.println(this.getString(WELCOME));
      }
    }
  }

  private void start()
  {
    while (true)
    {
      ChallengeDTO challenge = this.timeCalcCore.nextChallenge();
      this.printChallenge(challenge);
      TimeDTO answer = this.retrieveAnswer();
      System.out.print(this.getString(ANSWER_PREFIX));
      System.out.println(answer.toString());
      this.displayResult(answer);
      System.out.println(this.getString(CHALLENGE_SEPARATOR));
      this.waitForKeypress();
    }
  }

  private void printChallenge(ChallengeDTO challenge)
  {
    System.out.print(this.getString(QUESTION_PREFIX));
    System.out.print(challenge.getTime().toString());
    switch (this.timeCalcCore.getTimerDirection())
    {
      case DOWN:
        System.out.print(" - ");
        break;
      case UP:
        System.out.print(" + ");
        break;
      default:
        throw new AssertionError();
    }
    System.out.println(challenge.getItem().getDisplayText());
  }

  private TimeDTO retrieveAnswer()
  {
    while (true)
    {
      try
      {
        System.out.print(this.getString(ANSWER_PREFIX));
        String line = this.input.readLine();
        if (line == null)
        {
          continue;
        }
        line = line.trim();

        if (this.checkCommand(line))
        {
          continue;
        }
        else if (line.length() == 0)
        {
          continue;
        }
        else
        {
          try
          {
            return this.lineParser.parse(line);
          }
          catch (LineNotParseableException e)
          {
            System.out.println(e.getMessage());
          }
        }
        System.out.println(line);
      }
      catch (IOException e)
      {
        System.err.println(e.getMessage());
        continue;
      }
    }
  }

  private boolean checkCommand(String line)
  {
    if (line.length() == 1 && !Character.isDigit(line.toCharArray()[0]))
    {
      if (line.equalsIgnoreCase("q"))
      {
        System.exit(0);
      }
      else if (line.equalsIgnoreCase("c"))
      {
        this.stringFactory.loadCrypticBundle();
        System.out.println(this.getString(WELCOME));
      }
      else if (line.equalsIgnoreCase("e"))
      {
        this.stringFactory.loadBundleForLocale(Locale.ENGLISH);
        System.out.println(this.getString(WELCOME));
      }
      else if (line.equalsIgnoreCase("l"))
      {
        this.stringFactory.loadBundleForCurrentLocale();
        System.out.println(this.getString(WELCOME));
      }
      else if (line.equalsIgnoreCase("g"))
      {
        this.stringFactory.loadBundleForLocale(new Locale("de", "DE"));
        System.out.println(this.getString(WELCOME));
      }
      else if (line.equalsIgnoreCase("h"))
      {
        this.showHelp();
      }
      else if (line.equalsIgnoreCase("u"))
      {
        this.setTimerDirection(TimerDirection.UP);
      }
      else if (line.equalsIgnoreCase("d"))
      {
        this.setTimerDirection(TimerDirection.DOWN);
      }
      else
      {
        System.out.println(this.getString(UNKNOWN_COMMAND) + line);
      }
      return true;
    }
    else
    {
      return false;
    }
  }

  private void displayResult(TimeDTO answer)
  {
    boolean correct = this.timeCalcCore.checkAnswerForLastChallenge(answer);
    System.out.print("   ");
    if (correct)
    {
      System.out.print(this.getString(ANSWER_RIGHT));
    }
    else
    {
      System.out.print(this.getString(ANSWER_WRONG));
      TimeDTO correctAnswer = this.timeCalcCore.getAnswerForLastChallenge();
      System.out.print(this.getString(ANSWER_CORRECT_ANSWER));
      System.out.print(correctAnswer.toString());
      System.out.print(this.getString(ANSWER_WRONG_SEPERATOR));
    }
    System.out.print(this.timeCalcCore.getTimeNeededForLastAnswer());
    System.out.print(this.getString(ANSWER_SEPERATOR));
    System.out.print(this.timeCalcCore.getScoreForLastAnswer());
    System.out.print(this.getString(ANSWER_SEPERATOR));
    System.out.print(this.timeCalcCore.getScore());
    System.out.print(this.getString(ANSWER_SEPERATOR));
    System.out.print(this.timeCalcCore.getScore()
        / (float) this.timeCalcCore.getChallengesDone());
    System.out.println("");
  }

  private void showHelp()
  {
    System.out.println(HELP);
  }

  private void setTimerDirection(TimerDirection timerDirection)
  {
    this.timeCalcCore.setTimerDirection(timerDirection);
  }

  private void waitForKeypress()
  {
    System.out.println(this.getString(PRESS_ANY_KEY));
    try
    {
      this.input.readLine();
    }
    catch (IOException e)
    {
      System.err.println(e.getMessage());
    }
  }

  private String getString(String key)
  {
    return this.stringFactory.getString(key);
  }

  public static void main(String[] args)
  {
    Console console = new Console();
    console.start();
  }
}
